package it.maganar.lesscaos.session;

import java.sql.Time;
import java.util.ArrayList;
import java.util.List;

import it.maganar.lesscaos.entity.Disponibilita;
import it.maganar.lesscaos.entity.Negoziazione;
import it.maganar.lesscaos.entity.Partecipazione;
import it.maganar.lesscaos.entity.Tappa;
import it.maganar.lesscaos.entity.User;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Session Bean implementation class PartecipazioniManagerBean
 */
@SuppressWarnings("unchecked")
@Stateless
public class PartecipazioniManagerBean implements PartecipazioniManagerLocal {
    
	@PersistenceContext(unitName="lessDB")
	private EntityManager manager;
    
	/**
     * Default constructor. 
     */
    public PartecipazioniManagerBean() {
    	
    }

	@Override
	public void eliminaPartecipazione(int id, String nomeUtente) {
		Partecipazione p;
		p = manager.find(Partecipazione.class, id);
		if (p != null) {
			//cancello le negoziazioni associate
			List listaNegoziazioniAssociate = manager.createQuery("select N from Negoziazione as N join fetch N.proposta as P where P.id = ?1").setParameter(1, id).getResultList();
			for (Negoziazione n : (List<Negoziazione>)listaNegoziazioniAssociate) {
				manager.remove(n);
				manager.flush();
			}			
			User u = (User) manager.createQuery("select U from User U where U.nomeUtente =?1").setParameter(1, nomeUtente).getSingleResult();
			//controllo se l'utente ha delle partecipazioni
			if (!u.getListaPartecipazioni().isEmpty()) {
				u = (User) manager.createQuery("select U from User U join fetch U.listaPartecipazioni where U.nomeUtente =?1").setParameter(1, nomeUtente).getSingleResult();
				List<Partecipazione> listaPartecipazioni = (List<Partecipazione>)u.getListaPartecipazioni();
				for (int i = 0; i < listaPartecipazioni.size(); i++) {
					//cerco tra le partecipazioni dell'utente quella con l'id indicato
					if (listaPartecipazioni.get(i).getId() == id) {
						//se la trovo, la cancello dalla lista delle sue partecipazioni
						List listaNuova = new ArrayList();
						listaNuova = u.getListaPartecipazioni();
						listaNuova.remove(i);
						u.setListaPartecipazioni(listaNuova);
						manager.merge(u);
						manager.flush();
					}
				}
				//incremento di uno il numero di posti disponibili nella disponibilita associata
				if (p.getIscrizione().getNumPosti() < p.getIscrizione().getTipoVeicolo().posti()) {
					Disponibilita d = manager.find(Disponibilita.class, p.getIscrizione().getId());
					d.setNumPosti(d.getNumPosti() + 1);
					manager.persist(d);
					manager.flush();
				}
				manager.remove(p);
				manager.flush();
			}
		}
	}

	@Override
	public boolean inserisciPercorso(String nomeUtente, Disponibilita iscrizione, Tappa partenza, Tappa arrivo) {
		
		//la partecipazione è inserita nel DB ma non tra la lista delle partecipazioni dell'utente
		//sarà aggiunta al momento dell'accettazione della negoziazione associata
		Partecipazione toBeAdded = new Partecipazione();
		toBeAdded.setIscrizione(iscrizione);
		toBeAdded.setPartenza(partenza);
		toBeAdded.setArrivo(arrivo);
		
		if (verificaSovrapposizioni(nomeUtente, toBeAdded))
		{
			return false;
		}
		//decremento di uno il numero di posti disponibili nella disponibilita associata, se ci sono dei posti disponibili
		if (iscrizione.getNumPosti() == 0) {
			return false;
		} else {
			Disponibilita d = manager.find(Disponibilita.class, iscrizione.getId());
			d.setNumPosti(d.getNumPosti() - 1);
			manager.persist(d);
			manager.flush();
		}
		User u;
		u = manager.find(User.class, nomeUtente);
		toBeAdded.setRequestor(u);
		manager.persist(toBeAdded);
		manager.flush();
		
		// =========== una partecipaz non è aggiunta direttamente alla lista delle partecipazioni, passa prima dalle negoziazioni
		// qui l'ho fatta per testare l'aggiunta e l'eliminazione di una partecipazione

		//u.getListaPartecipazioni().add(toBeAdded);
		//manager.merge(u);
		//manager.flush();

		
		Negoziazione n = new Negoziazione();
		n.setCondizioni(new String());
		n.setStato(0);
		n.setTurno(0);
		n.setIssuer(toBeAdded.getIscrizione().getIssuer());
		n.setRequestor(u);
		n.setProposta(toBeAdded);
		manager.persist(n);
		manager.flush();
		return true;	
	}

	@Override
	public List ottieniListaPartecipazioni(String nomeUtente) {
		User u = (User) manager.createQuery("select U from User U where U.nomeUtente =?1").setParameter(1, nomeUtente).getSingleResult();
		//controllo se l'utente ha delle partecipazioni
		if (u.getListaPartecipazioni().isEmpty()) {
			//se non ne ha, ritorno una lista vuota
			return new ArrayList();
		} else {
			//altrimenti carico le sue parteciapzioni (se le caricassi subito nella prima query JBoss sclera!)
			//e le ritorno
			u = (User) manager.createQuery("select U from User U join fetch U.listaPartecipazioni where U.nomeUtente =?1").setParameter(1, nomeUtente).getSingleResult();
			List listaPartecipazioni = u.getListaPartecipazioni();
			return listaPartecipazioni;
		}		
	}

	@Override
	public List ottieniListaPercorsi(String partenza, String arrivo, boolean includi_parziali) {
		Query q_totali = manager.createQuery("select D from Disponibilita as D where (D.partenza.indirizzo = ?1 and D.arrivo.indirizzo = ?2)");
		q_totali.setParameter(1, partenza);
		q_totali.setParameter(2, arrivo);
		List results_totali = q_totali.getResultList();
		List results = new ArrayList();
		results.addAll(results_totali);
		if(includi_parziali)
		{
			Query q_parziali = manager.createQuery("select D from Disponibilita as D join D.listaTappe as T with T.indirizzo=?1 or T.indirizzo=?2 where (D.partenza.indirizzo = ?1 or D.arrivo.indirizzo = ?2)");// or D.partenza.indirizzo = ?2 or D.arrivo.indirizzo = ?1 or D.arrivo.indirizzo = ?2)) or count(*) >= 2");
			q_parziali.setParameter(1, partenza);
			q_parziali.setParameter(2, arrivo);
			List results_parziali = q_parziali.getResultList();
			q_parziali = manager.createQuery("select D from Disponibilita as D join D.listaTappe as T with T.indirizzo=?1 or T.indirizzo=?2 group by D having count(*) = 2");
			q_parziali.setParameter(1, partenza);
			q_parziali.setParameter(2, arrivo);
			results_parziali.addAll(q_parziali.getResultList());
			ArrayList<Disponibilita> toRemove = new ArrayList<Disponibilita>();
			for (Disponibilita d : (ArrayList<Disponibilita>) results_parziali)
			{
				boolean arrivo_found = false;
				for (Tappa t : (List<Tappa>) d.getListaTappe())
				{
					if (t.getIndirizzo().equals(arrivo))
						arrivo_found = true;
					else if (t.getIndirizzo().equals(partenza) && arrivo_found) 
					{
						toRemove.add(d);
					}
					else if (t.getIndirizzo().equals(partenza) && !arrivo_found)
						break;
				}
			}
			results_parziali.removeAll(toRemove);
			results.addAll(results_parziali);
		}
		
		return results;
	}
	
	private boolean verificaSovrapposizioni(String nomeUtente, Partecipazione pToCheck)
	{
		boolean pToCheckOcc;
		User u = manager.find(User.class, nomeUtente);
		List listaPartecipazioni = u.getListaPartecipazioni();
		List listaDisponibilita = u.getListaDisponibilita();
		Time orario_partenza = pToCheck.getPartenza().getOrario();
		Time orario_arrivo = pToCheck.getArrivo().getOrario();
		
		if (pToCheck.getIscrizione().getData() == null){
			pToCheckOcc = false;
		} else {
			pToCheckOcc = true;
		}
		
		boolean pOcc;
		boolean dOcc;
		
		if (listaPartecipazioni != null) {
			for (Partecipazione p : (List<Partecipazione>) listaPartecipazioni)
			{
				if (p.getIscrizione().getData() == null){
					pOcc = false;
				} else {
					pOcc = true;
				}
				
				Time orario_partenza_p = p.getPartenza().getOrario();
				Time orario_arrivo_p = p.getArrivo().getOrario();
				
				if ((orario_partenza_p.after(orario_partenza) && orario_partenza_p.before(orario_arrivo)) ||
					(orario_partenza.after(orario_partenza_p) && orario_partenza.before(orario_arrivo_p)) ||
					(orario_partenza.equals(orario_partenza_p)))
				{
					//sono qui se due orari si sovrappongono
					if(pToCheckOcc && pOcc){
						//se sono entremi occasionali la sovrapposizione d'orario non basta a dire che siano incompatibili
						if(pToCheck.getIscrizione().getData().equals(p.getIscrizione().getData())) {
							//segnalo la sovrapposizione solo se hanno la stessa data
							return true;
						}
					} else {
						//sono qui se almeno uno dei due non è occasionale, in questo caso la sovrapposizione d'orario
						//mi basta per dire che c'è sovrapposizione
						return true;
					}
				}
			}
		}
		
		if (listaDisponibilita != null) {
			for (Disponibilita d : (List<Disponibilita>) listaDisponibilita)
			{
				if (d.getData() == null){
					dOcc = false;
				} else {
					dOcc = true;
				}
				Time orario_partenza_d = d.getPartenza().getOrario();
				Time orario_arrivo_d = d.getArrivo().getOrario();
				
				if ((orario_partenza_d.after(orario_partenza) && orario_partenza_d.before(orario_arrivo)) ||
					(orario_partenza.after(orario_partenza_d) && orario_partenza.before(orario_arrivo_d)) ||
					(orario_partenza.equals(orario_partenza_d)))
				{
					//sono qui se due orari si sovrappongono
					if(pToCheckOcc && dOcc){
						//se sono entremi occasionali la sovrapposizione d'orario non basta a dire che siano incompatibili
						if(pToCheck.getIscrizione().getData().equals(d.getData())) {
							//segnalo la sovrapposizione solo se hanno la stessa data
							return true;
						}
					} else {
						//sono qui se almeno uno dei due non è occasionale, in questo caso la sovrapposizione d'orario
						//mi basta per dire che c'è sovrapposizione
						return true;
					}
				}
			}
		}
		
	return false;
	}

}
